//
// Globals.cc -- Contains C functions wrappers and startup/shutdown functions.
//

#include <Globals.h>
#include <Asm.h>
#include <Kernel.h>
#include <MonitorDriver.h>
#include <Multiboot.h>
#include <DescriptorTables.h>
#include <GenericFaultHandler.h>
#include <MemoryManagement.h>
#include <PageFaultHandler.h>
#include <Timer.h>
#include <ProcessManager.h>
#include <ElfParser.h>
#include <SerialDriver.h>
#include <SyscallDispatcher.h>
#include <Filesystem.h>
#include <InitrdFilesystem.h>
#include <IRQDispatcher.h>
#include <DebugExceptionHandler.h>
#include <ResourceManager.h>

extern u32int start_ctors;
extern u32int end_ctors;
extern u32int end;

MonitorDriver       monitor;
SerialDriver        serial;
DescriptorTables    descriptorTables;
Multiboot           multiboot;
GenericFaultHandler genericHandler;
PageFaultHandler    pageFaultHandler;
Kernel              kernel;
MemoryManagement    memoryManager;
Timer              *timer;
ProcessManager     *processManager;
Elf32Parser         kernelElfParser;
SyscallDispatcher   syscallDispatcher;
Filesystem         *filesystem;
InitrdFilesystem   *initrd;
IRQDispatcher      *irqDispatcher;
DebugExceptionHandler debugHandler;
ResourceManager     resourceManager;

// Required for GCC
void *__dso_handle;

extern "C" void __main()
{
  // Constructor list is defined in the linker script.
  u32int *iterator = (u32int *) &start_ctors;
  while (iterator < (u32int *)&end_ctors)
  {
    void (*fp)(void) = ( void (*)(void) )*iterator;
    fp();
    iterator += 4;
  }

}

extern "C" void __cxa_atexit(void (* f)(void *), void *p, void *d)
{
}

extern "C" int _main(MultibootHeader *multibootHeader)
{
  monitor.clear();
  multiboot = Multiboot(multibootHeader);

  kernel.run();
}

void *operator new(size_t size)
{
  if (memoryManager.isHeapInitialised())
  {
    return memoryManager.malloc(size);
  }
  else
  {
    u32int tmp = memoryManager.getPlacementAddress();
    memoryManager.setPlacementAddress(tmp+size);
    return (void *)tmp;
  }
}

void *operator new(size_t size, u32int place)
{
  if (!memoryManager.isHeapInitialised())
  {
    u32int tmp = memoryManager.getPlacementAddress();
    memoryManager.setPlacementAddress(tmp+size);
  }
  return (void *)place;
}

void *operator new(size_t size, bool pageAlign, u32int *addr)
{
  if (memoryManager.isHeapInitialised())
  {
    return memoryManager.malloc(size, pageAlign, addr);
  }
  else
  {
    if (pageAlign) {memoryManager.alignPlacementAddress();}
    u32int tmp = memoryManager.getPlacementAddress();
    if (addr) {*addr = tmp;}
    memoryManager.setPlacementAddress(tmp+size);
    return (void *)tmp;
  }
}

/**
  Carbon-copy of operator new(u32int).
**/
void *operator new[](size_t size)
{
  if (memoryManager.isHeapInitialised())
  {
    return memoryManager.malloc(size);
  }
  else
  {
    u32int tmp = memoryManager.getPlacementAddress();
    memoryManager.setPlacementAddress(tmp+size);
    return (void *)tmp;
  }
}

/**
  Carbon-copy of operator new(u32int,u32int*,bool).
**/
void *operator new[](size_t size, bool pageAlign, u32int *addr)
{
  if (memoryManager.isHeapInitialised())
  {
    return memoryManager.malloc(size, pageAlign, addr);
  }
  else
  {
    if (pageAlign) {memoryManager.alignPlacementAddress();}
    u32int tmp = memoryManager.getPlacementAddress();
    if (addr) {*addr = tmp;}
    memoryManager.setPlacementAddress(tmp+size);
    return (void *)tmp;
  }
}

void  operator delete(void *p)
{
  memoryManager.free(p);
}

void  operator delete[](void *p)
{
  memoryManager.free(p);
}

void panic(const char *message)
{
  stopInterrupts();

  kerr.write("[PANIC\t] ", lightRed);
  kerr.write("Kernel Panic: ", red);
  kerr.write((char*)message, red);
  kerr.write(".\n", red);
  kernel.printBacktrace();

  if (processManager->getProcess())
  {
    kerr.write("[PANIC\t] ", lightRed);
    kerr << "PID: " << processManager->getProcess()->getPid() << endl;
  }
  
#ifdef AUTO_RESTART
  // Automatically restart the machine.
#endif
  for (;;) ;
}

void panic_assert(const char *file, u32int line, const char *desc)
{

  kerr.write("[PANIC\t] ", lightRed);
  kerr.write("Kernel Panic - Assertion failure at ", red);
  kerr.write((char*)file, red);
  kerr.write(":", red);
  kerr.writeDec(line, red);
  kerr.write(" (", red);
  kerr.write(desc, red);
  kerr.write(")");
  kernel.printBacktrace();
  
  stopInterrupts();
  
#ifdef AUTO_RESTART
  // Automatically restart the machine.
#endif
  for (;;) ;
}

void memcpy(u8int *dest, u8int *src, u32int len)
{
  Kernel::memcpy(dest, src, len);
}

int strcmp(char *str1, char *str2)
{
  Kernel::strcmp(str1, str2);
}

extern "C" void *umalloc(u32int size)
{
  return memoryManager.umalloc(size);
}

extern "C" void ufree(void *p)
{
  memoryManager.ufree(p);
}
